#!/usr/bin/env python3
"""Generate FakeConfiguration.gen.h from plugin/contents/config/main.xml.

Emits a QObject with one Q_PROPERTY(<type> <Name> MEMBER m_<Name> NOTIFY <Name>Changed)
per <entry>, so the fake `wallpaper.configuration` fires per-key change signals that
main.qml's reactive Connections rely on. Fails loudly on an unknown KConfigXT type."""
import sys
import xml.etree.ElementTree as ET

TYPE_MAP = {"Bool": "bool", "Int": "int", "Double": "double",
            "String": "QString", "Path": "QString"}


def _local(tag):
    return tag.split('}')[-1]


def cpp_default(cpptype, raw):
    raw = (raw or "").strip()
    if cpptype == "bool":
        return "true" if raw.lower() == "true" else "false"
    if cpptype == "int":
        return raw if raw else "0"
    if cpptype == "double":
        return raw if raw else "0.0"
    return f'QStringLiteral("{raw}")' if raw else "QString()"


def parse_entries(xml_path):
    root = ET.parse(xml_path).getroot()
    entries = []
    for entry in root.iter():
        if _local(entry.tag) != "entry":
            continue
        name, ktype = entry.get("name"), entry.get("type")
        if not name or not ktype:
            continue
        if ktype not in TYPE_MAP:
            sys.exit(f"gen_fake_config: unknown KConfigXT type {ktype!r} for "
                     f"entry {name!r}; extend TYPE_MAP")
        cpptype = TYPE_MAP[ktype]
        raw = ""
        for child in entry:
            if _local(child.tag) == "default":
                raw = child.text or ""
                break
        entries.append((name, cpptype, cpp_default(cpptype, raw)))
    return entries


def notify_sig(name):
    # QML resolves handler `onDisplayModeChanged` to a signal `displayModeChanged`
    # (it lowercases the first letter after stripping `on`). The property name stays
    # PascalCase (read as `configuration.DisplayMode`); only the NOTIFY signal name
    # is lowercase-first so main.qml's reactive Connections actually bind.
    return name[0].lower() + name[1:] + "Changed"


def render(entries):
    props = "\n".join(
        f"    Q_PROPERTY({t} {n} MEMBER m_{n} NOTIFY {notify_sig(n)})" for n, t, _ in entries)
    sigs = "\n".join(f"    void {notify_sig(n)}();" for n, _, _ in entries)
    mems = "\n".join(f"    {t} m_{n} = {d};" for n, t, d in entries)
    return f"""// AUTO-GENERATED by tools/genconfig/gen_fake_config.py from main.xml. DO NOT EDIT.
#pragma once
#include <QObject>
#include <QString>

class FakeConfiguration : public QObject {{
    Q_OBJECT
{props}
public:
    explicit FakeConfiguration(QObject *parent = nullptr) : QObject(parent) {{}}
signals:
{sigs}
private:
{mems}
}};
"""


def main():
    if len(sys.argv) != 3:
        sys.exit("usage: gen_fake_config.py <main.xml> <out.h>")
    entries = parse_entries(sys.argv[1])
    if not entries:
        sys.exit("gen_fake_config: no <entry> elements found")
    with open(sys.argv[2], "w") as f:
        f.write(render(entries))
    print(f"gen_fake_config: wrote {len(entries)} properties to {sys.argv[2]}")


if __name__ == "__main__":
    main()
